#include "basesurface.h"

#include <QBitmap>
#include <QDebug>
#include <QQuickWindow>

#include "opencv2/opencv.hpp"
#include "shaderloader.h"

extern "C" {
#include <libavcodec/avcodec.h>
#include <libavdevice/avdevice.h>
}

static const GLfloat vertices[] = {
    // 顶点坐标
    -1.0, -1.0, 1.0,  // 左下
    -1.0, 1.0, 1.0,   // 左上
    1.0, 1.0, 1.0,    // 右上
    1.0, -1.0, 1.0,   // 右下

    // 纹理坐标
    0.0f, 1.0f,  // 左上
    0.0f, 0.0f,  // 左下角
    1.0f, 0.0f,  // 右下角
    1.0f, 1.0f,  // 右上

};

Render::Render()
    : m_texRgb{QOpenGLTexture::Target2D},
      m_texY{QOpenGLTexture::Target2D},
      m_texU{QOpenGLTexture::Target2D},
      m_texV{QOpenGLTexture::Target2D},
      m_texMask{QOpenGLTexture::Target2D} {
  initializeOpenGLFunctions();
}

void Render::setViewportSize(const QSize &size) { m_windowSize = size; }

void Render::setWindow(QQuickWindow *win) { m_pWindow = win; }

void Render::setLastImg(std::shared_ptr<AVFrame> img, eImageType type) {
  m_imgType = type;
  m_spLastFrame = img;
}

bool Render::init() {
  if (!m_pProgram) {
    // 创建对象
    m_vao.create();
    m_vbo.create();
    m_texRgb.create();
    m_texY.create();
    m_texU.create();
    m_texV.create();
    m_texMask.create();

    // 绑定
    m_vao.bind();
    m_vbo.bind();
    m_vbo.allocate(vertices, sizeof(vertices));

    m_pProgram = new QOpenGLShaderProgram();
    m_pProgram->addShaderFromSourceFile(QOpenGLShader::Vertex,
                                        ShaderLoader::getVert());
    m_pProgram->addShaderFromSourceFile(QOpenGLShader::Fragment,
                                        ShaderLoader::getFrag());
    m_pProgram->link();

    // 设置顶点属性
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_TRUE, 3 * sizeof(GLfloat),
                          (void *)0);
    glEnableVertexAttribArray(0);

    // 纹理坐标
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_TRUE, 2 * sizeof(GLfloat),
                          (void *)(12 * sizeof(GLfloat)));
    glEnableVertexAttribArray(1);

    m_vbo.release();
    m_vao.release();

    // 初始化纹理
    //        glActiveTexture(GL_TEXTURE0);
    //        m_texRgb.bind(0);

    // 蒙版纹理加载
    QImage mask("./star_mask.png");
    m_texMask.bind();
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    m_texMask.setData(mask);

    m_texMask.release();

    m_iFrameCountTimer = startTimer(1000);
  }
  return true;
}

void Render::paint() {
  glDisable(GL_DEPTH_TEST);
  glViewport(0, 0, m_windowSize.width(), m_windowSize.height());

  // 清屏
  glClearColor(.2, .3, .3, 1.);
  glClear(GL_COLOR_BUFFER_BIT);

  m_vao.bind();

  if (m_spLastFrame) {
    m_pProgram->bind();

    // 更新纹理数据
    updateTex();

    m_pProgram->setUniformValue("tex_rgb", 5);
    m_pProgram->setUniformValue("tex_y", 1);
    m_pProgram->setUniformValue("tex_u", 2);
    m_pProgram->setUniformValue("tex_v", 3);
    m_pProgram->setUniformValue("tex_mask", 4);

    glDrawArrays(GL_TRIANGLE_FAN, 0, 4);

    // 统计画面帧
    ++m_iFrameCount;

    // 解绑，很关键。不然可能会影响到qml其他组件的绘制
    m_texRgb.release();
    m_texY.release();
    m_texU.release();
    m_texV.release();
    m_texMask.release();
  }

  m_vao.release();
  m_pProgram->release();
  m_pWindow->resetOpenGLState();
}

// 定时器
void Render::timerEvent(QTimerEvent *event) {
  if (event->timerId() == m_iFrameCountTimer) {
    qDebug() << "fps:" << m_iFrameCount;
    m_iFrameCount = 0;
  }
}

void Render::updateTex() {
  // rgb 和 yuv分开设置纹理
  if (eImageType::RGB888 == m_imgType) {
    glActiveTexture(GL_TEXTURE5);
    m_texRgb.bind();
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, m_spLastFrame->width,
                 m_spLastFrame->height, 0, GL_RGB, GL_UNSIGNED_BYTE,
                 m_spLastFrame->data[0]);
    m_pProgram->setUniformValue("use_rgb", true);
  } else {
    glActiveTexture(GL_TEXTURE1);
    m_texY.bind();
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, m_spLastFrame->width,
                 m_spLastFrame->height, 0, GL_RED, GL_UNSIGNED_BYTE,
                 m_spLastFrame->data[0]);

    glActiveTexture(GL_TEXTURE2);
    m_texU.bind();
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, m_spLastFrame->width >> 1,
                 m_spLastFrame->height, 0, GL_RED, GL_UNSIGNED_BYTE,
                 m_spLastFrame->data[1]);

    glActiveTexture(GL_TEXTURE3);
    m_texV.bind();
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, m_spLastFrame->width >> 1,
                 m_spLastFrame->height, 0, GL_RED, GL_UNSIGNED_BYTE,
                 m_spLastFrame->data[2]);
    m_pProgram->setUniformValue("use_rgb", false);
  }

  glActiveTexture(GL_TEXTURE4);
  m_texMask.bind();
}

void Render::record() {
  // 如果画面大小不是录制所需大小 则不录制 暂时固定为1920*1080
  if (m_windowSize.width() != 1920 || m_windowSize.height() != 1080) {
    return;
  }

  auto img = std::make_shared<QImage>(
      m_windowSize.width(), m_windowSize.height(), QImage::Format_RGB888);

  //    glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
  glReadPixels(0, 0, img->width(), img->height(), GL_RGB, GL_UNSIGNED_BYTE,
               img->bits());

  // 发送显存中获取到的图像
  emit this->captured(img);
}

/************* BaseSurface **************/
BaseSurface::BaseSurface() {
  connect(this, &QQuickItem::windowChanged, this,
          &BaseSurface::handleWindowChanged);
}

void BaseSurface::handleWindowChanged(QQuickWindow *win) {
  if (win) {
    connect(win, &QQuickWindow::beforeRendering, this, &BaseSurface::sync,
            Qt::DirectConnection);
    win->setClearBeforeRendering(false);
  }
}

void BaseSurface::sync() {
  if (!m_pRender) {
    m_pRender = new Render();

    connect(window(), &QQuickWindow::beforeRendering, m_pRender, &Render::paint,
            Qt::DirectConnection);
    m_pRender->init();
    connect(window(), &QQuickWindow::afterRendering, m_pRender, &Render::record,
            Qt::DirectConnection);

// 设置编码器
#ifdef __aarch64__
    m_pRecoder = new VideoRecorder_MPP;
#else
    m_pRecoder = new VideoRecorder_ffmpeg;
#endif
    if (!m_pRecoder->init()) {
      qDebug() << "init video recoder failed";
    }

    m_pRecoder->openFile("./replay.h264");

    connect(m_pRender, &Render::captured, m_pRecoder, &BaseRecorder::onImage,
            Qt::QueuedConnection);
  }
  m_pRender->setViewportSize(window()->size() * window()->devicePixelRatio());

  m_pRender->setWindow(window());
  m_pRender->setLastImg(m_spLastFrame, m_imgType);
}

void BaseSurface::updateSurface(std::shared_ptr<AVFrame> img) {
  if (img->format == AVPixelFormat::AV_PIX_FMT_YUVJ422P) {
    m_imgType = eImageType::YUV422;
  } else if (img->format == AVPixelFormat::AV_PIX_FMT_RGB24) {
    m_imgType = eImageType::RGB888;
  } else {
    qDebug() << "unknown format" << img->format;
  }

  m_spLastFrame = img;
  if (window()) {
    window()->update();
  }
}

void BaseSurface::setSource(VideoCapturer *src) {
  if (src) {
    connect(src, &VideoCapturer::appending_frame, this,
            &BaseSurface::updateSurface, Qt::QueuedConnection);
  }
}
